import UserDict

import anydbm

try:
    from cPickle import Pickler, Unpickler
except ImportError:
    from pickle import Pickler, Unpickler

try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO

__all__=('AbstractStorage', 'MemoryStorage', 'DbmStorage')

class AbstractStorage(UserDict.DictMixin): # TODO: add schemas support
    """Base class for storage objects, storage objects are
    a way to let pymsn and the client agree on how data may
    be stored. This data included security tokens, cached
    display pictures ..."""

    def __init__(self, identifier):
        """Initializer
        
        @param identifier: the identifier of this storage instance
        @type identifier: string"""
        self.storage_id = identifier
    
    def keys(self):
        raise NotImplementedError("Abstract method call")
    
    def has_key(self, key):
        return key in self.keys()

    def get(self, key, default=None):
        if self.has_key(key):
            return self[key]
        return default

    def __len__(self):
        return len(self.keys)

    def __contains__(self, key):
        return self.has_key(key)

    def __getitem__(self, key):
        raise NotImplementedError("Abstract method call")

    def __setitem__(self, key, value):
        raise NotImplementedError("Abstract method call")

    def __delitem__(self, key):
        raise NotImplementedError("Abstract method call")

    def __del__(self):
        raise NotImplementedError("Abstract method call")

    def close(self):
        pass


_MemoryStorageDict = {}
class MemoryStorage(AbstractStorage):
    """In memory storage type"""
    
    def __init__(self, identifier):
        AbstractStorage.__init__(self, identifier)
        if identifier not in _MemoryStorageDict:
            _MemoryStorageDict[identifier] = {}

    def keys(self):
        return _MemoryStorageDict[self.storage_id].keys()
    
    def has_key(self, key):
        return key in self.keys()

    def __len__(self):
        return len(self.keys)

    def __contains__(self, key):
        return self.has_key(key)

    def __getitem__(self, key):
        return _MemoryStorageDict[self.storage_id][key]

    def __setitem__(self, key, value):
        _MemoryStorageDict[self.storage_id][key] = value

    def __delitem__(self, key):
        del _MemoryStorageDict[self.storage_id][key]

    def __del__(self):
        pass

    def close(self):
        pass


class DbmStorage(AbstractStorage):

    PICKLING_PROTOCOL = -1 #use the highest possible version

    def __init__(self, filename):
        AbstractStorage.__init__(self, filename)
        self._dict = anydbm.open(filename, 'c')
    
    def keys(self):
        return self._dict.keys()
    
    def has_key(self, key):
        return self._dict.has_key()

    def __len__(self):
        return len(self._dict)

    def __contains__(self, key):
        return self._dict.has_key()

    def __getitem__(self, key):
        f = StringIO(self._dict[str(key)]) # some dbm don't support int keys
        return Unpickler(f).load()

    def __setitem__(self, key, value):
        f = StringIO()
        pickler = Pickler(f, self.PICKLING_PROTOCOL)
        pickler.dump(value)
        self._dict[str(key)] = f.getvalue()
        if hasattr(self._dict, 'sync'):
            self._dict.sync()

    def __delitem__(self, key):
        del self._dict[str(key)]

    def __del__(self):
        self.close()
    
    def close(self):
        if hasattr(self._dict, 'sync'):
            self._dict.sync()
        self._dict.close()

